// 版权所有2009年围棋作者。版权所有。
// 此源代码的使用受BSD样式的约束
// 可以在许可证文件中找到的许可证。

package rsa

import (
	"crypto"
	"crypto/subtle"
	"errors"
	"io"
	"math/big"

	"crypto/internal/randutil"
)

// 此文件使用PKCS#1 v1.5填充实现加密和解密。

// PKCS1v15DecrypterOpts用于使用
// 加密解密程序接口。
type PKCS1v15DecryptOptions struct {
	// SessionKeyLen是正在处理的会话密钥的长度
	// 解密。如果不是零，则解密期间将出现填充错误
	// 导致返回此长度的随机明文，而不是
	// 一个错误。这些选择在固定的时间内发生。
	SessionKeyLen int
}

// EncryptPKCS1v15使用RSA和填充对给定消息进行加密
// 来自PKCS#1 v1.5的方案。消息的长度不得超过
// 公共模数的长度减去11字节。
// None
// rand参数用作熵源，以确保
// 对同一消息加密两次不会产生相同的结果
// 密文。
// None
// 警告：使用此函数加密除
// 会话密钥是危险的。在新协议中使用RSA OAEP。
func EncryptPKCS1v15(rand io.Reader, pub *PublicKey, msg []byte) ([]byte, error) {
	randutil.MaybeReadByte(rand)

	if err := checkPub(pub); err != nil {
		return nil, err
	}
	k := pub.Size()
	if len(msg) > k-11 {
		return nil, ErrMessageTooLong
	}

	// EM=0x00 | | 0x02 | | PS | | 0x00 | | M
	em := make([]byte, k)
	em[1] = 2
	ps, mm := em[2:len(em)-len(msg)-1], em[len(em)-len(msg):]
	err := nonZeroRandomBytes(ps, rand)
	if err != nil {
		return nil, err
	}
	em[len(em)-len(msg)-1] = 0
	copy(mm, msg)

	m := new(big.Int).SetBytes(em)
	c := encrypt(new(big.Int), pub, m)

	return c.FillBytes(em), nil
}

// DecryptPKCS1v15使用RSA和PKCS#1v1.5中的填充方案对明文进行解密。
// 如果兰德！=nil，它使用RSA盲法来避免定时侧通道攻击。
// None
// 请注意，此函数是否返回错误会泄露秘密
// 信息如果攻击者能够导致此函数重复运行，并且
// 了解每个实例是否返回了一个错误，然后可以对其进行解密和
// 伪造签名，就好像他们有私钥一样。看见
// 解密PKCS1V15SessionKey以获取解决此问题的方法。
func DecryptPKCS1v15(rand io.Reader, priv *PrivateKey, ciphertext []byte) ([]byte, error) {
	if err := checkPub(&priv.PublicKey); err != nil {
		return nil, err
	}
	valid, out, index, err := decryptPKCS1v15(rand, priv, ciphertext)
	if err != nil {
		return nil, err
	}
	if valid == 0 {
		return nil, ErrDecryption
	}
	return out[index:], nil
}

// DecryptPKCS1v15SessionKey使用RSA和PKCS#1v1.5中的填充方案对会话密钥进行解密。
// 如果兰德！=nil，它使用RSA盲法来避免定时侧通道攻击。
// 如果密文的长度错误，或者
// 密文大于公共模数。否则，不会出现错误
// 返回。如果填充有效，则复制生成的明文消息
// 把钥匙打开。否则，键将保持不变。这些替代方案经常出现
// 时间该函数的用户将生成一个随机变量
// 会话密钥，并使用结果值继续协议。
// 这将消除攻击者获取任何信息的可能性
// 关于明文。
// 请参阅``针对基于RSA的协议的选定密文攻击
// 加密标准PKCS#1''，Daniel Bleichenbacher，密码学进展
// （Crypto'98）。
// None
// 请注意，如果会话密钥太小，则可能会发生错误
// 攻击者用蛮力攻击它。如果他们能做到这一点，那么他们就可以知道
// 使用了一个随机值（因为同一密文的值不同）
// 从而判断填充是否正确。这违背了这一点
// 作用使用至少16字节的密钥可以防止此攻击。
func DecryptPKCS1v15SessionKey(rand io.Reader, priv *PrivateKey, ciphertext []byte, key []byte) error {
	if err := checkPub(&priv.PublicKey); err != nil {
		return err
	}
	k := priv.Size()
	if k-(len(key)+3+8) < 0 {
		return ErrDecryption
	}

	valid, em, index, err := decryptPKCS1v15(rand, priv, ciphertext)
	if err != nil {
		return err
	}

	if len(em) != k {
		// 这应该是不可能的，因为PKCS1V15总是
		// 返回完整的切片。
		return ErrDecryption
	}

	valid &= subtle.ConstantTimeEq(int32(len(em)-index), int32(len(key)))
	subtle.ConstantTimeCopy(valid, key, em[len(em)-len(key):])
	return nil
}

// decryptPKCS1v15使用priv对密文进行解密，如果
// 兰德不是零。它在valid中返回一或零，指示
// 明文的结构是正确的。无论哪种情况，明文都是
// 在em中返回，以便可以独立于其是否有效进行读取
// 以保持恒定的内存访问模式。如果明文是
// valid then index包含em中原始邮件的索引。
func decryptPKCS1v15(rand io.Reader, priv *PrivateKey, ciphertext []byte) (valid int, em []byte, index int, err error) {
	k := priv.Size()
	if k < 11 {
		err = ErrDecryption
		return
	}

	c := new(big.Int).SetBytes(ciphertext)
	m, err := decrypt(rand, priv, c)
	if err != nil {
		return
	}

	em = m.FillBytes(make([]byte, k))
	firstByteIsZero := subtle.ConstantTimeByteEq(em[0], 0)
	secondByteIsTwo := subtle.ConstantTimeByteEq(em[1], 2)

	// 纯文本的其余部分必须是非零随机字符串
	// 八位字节，后跟0，后跟消息。
	// lookingForIndex:1如果我们仍在寻找零。
	// 索引：第一个零字节的偏移量。
	lookingForIndex := 1

	for i := 2; i < len(em); i++ {
		equals0 := subtle.ConstantTimeByteEq(em[i], 0)
		index = subtle.ConstantTimeSelect(lookingForIndex&equals0, i, index)
		lookingForIndex = subtle.ConstantTimeSelect(equals0, 0, lookingForIndex)
	}

	// PS填充长度必须至少为8字节，并且从两个字节开始
	// 字节转换为em。
	validPS := subtle.ConstantTimeLessOrEq(2+8, index)

	valid = firstByteIsZero & secondByteIsTwo & (^lookingForIndex & 1) & validPS
	index = subtle.ConstantTimeSelect(valid, index+1, 0)
	return valid, em, index, nil
}

// nonZeroRandomBytes使用非零随机八位字节填充给定的片。
func nonZeroRandomBytes(s []byte, rand io.Reader) (err error) {
	_, err = io.ReadFull(rand, s)
	if err != nil {
		return
	}

	for i := 0; i < len(s); i++ {
		for s[i] == 0 {
			_, err = io.ReadFull(rand, s[i:i+1])
			if err != nil {
				return
			}
			// 在测试中，PRNG可能会返回所有零，所以我们会这样做
			// 这有助于打破循环。
			s[i] ^= 0x42
		}
	}

	return
}

// 这些是ASN1-DER结构：
// DigestInfo:：=序列{
// 算法识别器，
// 摘要八进制字符串
// }
// 为了提高性能，我们不使用通用ASN1编码器。相反，我们
// 预计算生成有效ASN1 DER字符串的摘要值的前缀
// 内容正确。
var hashPrefixes = map[crypto.Hash][]byte{
	crypto.MD5:       {0x30, 0x20, 0x30, 0x0c, 0x06, 0x08, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x02, 0x05, 0x05, 0x00, 0x04, 0x10},
	crypto.SHA1:      {0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1a, 0x05, 0x00, 0x04, 0x14},
	crypto.SHA224:    {0x30, 0x2d, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x04, 0x05, 0x00, 0x04, 0x1c},
	crypto.SHA256:    {0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20},
	crypto.SHA384:    {0x30, 0x41, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02, 0x05, 0x00, 0x04, 0x30},
	crypto.SHA512:    {0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05, 0x00, 0x04, 0x40},
	crypto.MD5SHA1:   {}, // 不使用ASN1前缀的特殊TLS案例。
	crypto.RIPEMD160: {0x30, 0x20, 0x30, 0x08, 0x06, 0x06, 0x28, 0xcf, 0x06, 0x03, 0x00, 0x31, 0x04, 0x14},
}

// SignPKCS1v15使用
// RSASSA-PKCS1-V1#u 5-来自RSA PKCS#1 V1.5的签名。注意，散列必须
// 是使用给定哈希对输入消息进行哈希处理的结果
// 作用如果散列为零，则直接对散列进行签名。这不是
// 除互操作性外，建议使用。
// None
// 如果rand不是nil，则将使用RSA盲法来避免计时
// 侧通道攻击。
// None
// 这个函数是确定性的。因此，如果可能的话
// 消息很小，攻击者可以从中构建映射
// 将消息转换为签名，并标识已签名的消息。一如既往，
// 签名提供真实性，而不是保密性。
func SignPKCS1v15(rand io.Reader, priv *PrivateKey, hash crypto.Hash, hashed []byte) ([]byte, error) {
	hashLen, prefix, err := pkcs1v15HashInfo(hash, len(hashed))
	if err != nil {
		return nil, err
	}

	tLen := len(prefix) + hashLen
	k := priv.Size()
	if k < tLen+11 {
		return nil, ErrMessageTooLong
	}

	// EM=0x00 | | 0x01 | | PS | | 0x00 | | T
	em := make([]byte, k)
	em[1] = 1
	for i := 2; i < k-tLen-1; i++ {
		em[i] = 0xff
	}
	copy(em[k-tLen:k-hashLen], prefix)
	copy(em[k-hashLen:k], hashed)

	m := new(big.Int).SetBytes(em)
	c, err := decryptAndCheck(rand, priv, m)
	if err != nil {
		return nil, err
	}

	return c.FillBytes(em), nil
}

// VerifyPKCS1v15验证RSA PKCS#1 v1.5签名。
// hashed是使用给定哈希对输入消息进行哈希处理的结果
// 函数和sig是签名。有效签名由以下符号表示：
// 返回零错误。如果hash为零，则直接使用hash。这
// 除了互操作性之外，不建议使用。
func VerifyPKCS1v15(pub *PublicKey, hash crypto.Hash, hashed []byte, sig []byte) error {
	hashLen, prefix, err := pkcs1v15HashInfo(hash, len(hashed))
	if err != nil {
		return err
	}

	tLen := len(prefix) + hashLen
	k := pub.Size()
	if k < tLen+11 {
		return ErrVerification
	}

	// RFC 8017第8.2.2节：如果签名的长度不是k
	// 八位字节（其中k是RSA模n的八位字节长度），输出
	// “无效签名”并停止。
	if k != len(sig) {
		return ErrVerification
	}

	c := new(big.Int).SetBytes(sig)
	m := encrypt(new(big.Int), pub, c)
	em := m.FillBytes(make([]byte, k))
	// EM=0x00 | | 0x01 | | PS | | 0x00 | | T

	ok := subtle.ConstantTimeByteEq(em[0], 0)
	ok &= subtle.ConstantTimeByteEq(em[1], 1)
	ok &= subtle.ConstantTimeCompare(em[k-hashLen:k], hashed)
	ok &= subtle.ConstantTimeCompare(em[k-tLen:k-hashLen], prefix)
	ok &= subtle.ConstantTimeByteEq(em[k-tLen-1], 0)

	for i := 2; i < k-tLen-1; i++ {
		ok &= subtle.ConstantTimeByteEq(em[i], 0xff)
	}

	if ok != 1 {
		return ErrVerification
	}

	return nil
}

func pkcs1v15HashInfo(hash crypto.Hash, inLen int) (hashLen int, prefix []byte, err error) {
	// 特殊情况：crypto.Hash（0）用于指示数据为
	// 直接签名。
	if hash == 0 {
		return inLen, nil, nil
	}

	hashLen = hash.Size()
	if inLen != hashLen {
		return 0, nil, errors.New("crypto/rsa: input must be hashed message")
	}
	prefix, ok := hashPrefixes[hash]
	if !ok {
		return 0, nil, errors.New("crypto/rsa: unsupported hash function")
	}
	return
}
